﻿using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using CRM.Anyleads;
using CRM.Models.Email;
using DnsClient;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;

namespace CRM.Services
{
    public class AnyleadsService : BaseService
    {
        private readonly AnyleadsClient _anyleadsClient;
        private readonly MailVerifierClient _mailVerifierClient;
        private readonly MyDbContext _dbContext;
        private readonly EmailDogClient _emailDogClient;
        private readonly ILogger<AnyleadsService> _logger;

        public AnyleadsService(AnyleadsClient anyleadsClient, MailVerifierClient mailVerifierClient,
            MyDbContext dbContext, EmailDogClient emailDogClient, ILogger<AnyleadsService> logger)
        {
            _anyleadsClient = anyleadsClient;
            _mailVerifierClient = mailVerifierClient;
            _dbContext = dbContext;
            _emailDogClient = emailDogClient;
            _logger = logger;
        }

        /// <summary>
        /// 验证邮箱有效性并缓存
        /// </summary>
        /// <param name="email"></param>
        /// <returns></returns>
        public async Task<bool> ValidateEmail(string email)
        {
            var result = await ValidateEmail(new string[] { email });
            return result[email];
        }

        private Task<bool> ValidateEmailByEmailDogCore(string email)
        {
            return _emailDogClient.VerifyEmailState(email);
            ;
        }

        private async Task<bool> ValidateEmailByMxCore(string domain)
        {
            var lookup = new LookupClient();
            var ct = new CancellationTokenSource();
            var result = await lookup.QueryAsync(domain, QueryType.MX, QueryClass.IN, ct.Token);
            ct.CancelAfter(3_000);

            if (result.HasError)
                return false;

            var flag = result.Answers.MxRecords().Any();
            _logger.LogDebug("检测域名{Domain}的MX值为{Flag}", domain, flag);
            return flag;
        }


        /// <summary>
        /// 批量验证邮箱有效性并缓存
        /// </summary>
        /// <param name="emailList"></param>
        /// <returns></returns>
        public Task<Dictionary<string, bool>> ValidateEmail(IEnumerable<string> emailList)
        {
            return CurrentUser.OemId == 9 /*|| CurrentUser.OemId == 1*/
                ? ValidateEmailByMx(emailList)
                : ValidateEmailByThirdParty(emailList);
        }

        private async Task<Dictionary<string, bool>> ValidateEmailByThirdParty(IEnumerable<string> emailList)
        {
            var distinctEmailList = emailList.Distinct().ToArray();
            var emailVerifies = await _dbContext.EmailVerifies
                .Where(e => distinctEmailList.Contains(e.EmailAddress))
                .Select(e => new EmailVerify(){ Id = e.Id, EmailAddress = e.EmailAddress, State = e.State })
                .ToListAsync();
            emailVerifies = emailVerifies.DistinctBy(e => e.EmailAddress).ToList();
            var diffEmailList = distinctEmailList
                .Except(emailVerifies.Where(e => e.State != EmailVerifyState.NeedReverify).Select(e => e.EmailAddress))
                .ToList();

            // emailAddress去重
            var resDic = emailVerifies.Where(e => e.State != EmailVerifyState.NeedReverify)
                .ToDictionary(x => x.EmailAddress, x => x.State == EmailVerifyState.Valid);
            if (diffEmailList.Any())
            {
                var emailVerifiesDic = emailVerifies.ToDictionary(x => x.EmailAddress, x => x);
                foreach (var diff in diffEmailList)
                {
                    var state = await _mailVerifierClient.VerifyEmailState(diff);
                    if (emailVerifiesDic.TryGetValue(diff, out var emailVerify))
                    {
                        emailVerify.State = state;
                    }
                    else
                    {
                        emailVerify = new EmailVerify()
                        {
                            OemId = CurrentUser.OemId,
                            CompanyId = CurrentUser.CompanyId,
                            UserId = CurrentUser.Id,
                            GroupId = CurrentUser.GroupId,
                            EmailAddress = diff,
                            State = state,
                        };
                        _dbContext.Add(emailVerify);
                    }
                    
                    resDic.Add(diff, state == EmailVerifyState.Valid);
                }
            }

            await _dbContext.SaveChangesAsync();
            await AddEmailVerifyLog(distinctEmailList);
            return resDic;
        }

        private async Task<Dictionary<string, bool>> ValidateEmailByMx(IEnumerable<string> emailList)
        {
            var distinctEmailList = emailList.Distinct().ToArray();

            var domains = distinctEmailList.Select(e =>
            {
                var split = e.Split('@', 2);
                return split.Length < 2 ? "" : split[1];
            }).Distinct().Where(d => !string.IsNullOrEmpty(d)).ToArray();

            // emailAddress去重
            var resDic = new Dictionary<string, bool>();
            if (domains.Any())
            {
                var domainDic = new Dictionary<string, bool>();
                foreach (var domain in domains)
                {
                    var result = await ValidateEmailByMxCore(domain);
                    domainDic.Add(domain, result);
                }

                foreach (var diff in distinctEmailList)
                {
                    var split = diff.Split('@', 2);
                    var result = false;
                    if (split.Length == 2)
                    {
                        var domain = split[1];
                        domainDic.TryGetValue(domain, out result);
                    }

                    resDic.Add(diff, result);
                    // _dbContext.Add(emailVerify);
                }
            }

            await _dbContext.SaveChangesAsync();
            // await AddEmailVerifyLog(distinctEmailList);
            return resDic;
        }

        private async Task AddEmailVerifyLog(IEnumerable<string> emailList)
        {
            var emailVerifyLogs = await _dbContext.EmailVerifyLogs.Where(m =>
                    m.OemId == CurrentUser.OemId && m.CompanyId == CurrentUser.CompanyId &&
                    m.GroupId == CurrentUser.GroupId &&
                    m.UserId == CurrentUser.Id)
                .Where(e => emailList.Contains(e.EmailAddress))
                .ToListAsync();
            var diffLogList = emailList.Except(emailVerifyLogs.Select(e => e.EmailAddress)).ToList();
            var diffEmailVerifies = await _dbContext.EmailVerifies.Where(e => diffLogList.Contains(e.EmailAddress))
                .Select(e => new EmailVerifyLog()
                {
                    OemId = CurrentUser.OemId,
                    CompanyId = CurrentUser.CompanyId,
                    UserId = CurrentUser.Id,
                    GroupId = CurrentUser.GroupId,
                    EmailAddress = e.EmailAddress,
                    State = e.State
                }).ToListAsync();
            await _dbContext.AddRangeAsync(diffEmailVerifies);
            await _dbContext.SaveChangesAsync();
        }
    }
}